﻿using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Main
{


    delegate void OnClientConnected(MainServer instance, Socket socket);

    delegate void OnClientLostConnection(MainServer instance,ClientHolder client);

    delegate void OnClientUpdateUI(MainServer instance,ClientHolder client);

    delegate void OnServerStarted();

    delegate void OnServerClosed();

    delegate void OnSendingStatusChanged(int current,int max);


    class MainServer
    {

        private static int RECEIVE_BUFFER_SIZE = 1024 * 1024 * 8; //8MB
        private static string tempLocalCmdString = string.Empty;
        private Thread ListenThread;
        private Thread ReceiveDataThread;
        private object ProcessClientCmdLocker = new object();

        private Socket ServerSocket;

        private List<ClientHolder> clients = new List<ClientHolder>();

        private static MainServer _instance = new MainServer();

        /// <summary>
        /// 单例
        /// </summary>
        /// <returns></returns>
        public static MainServer Instance()
        {
            return _instance;
        }


        public string ServerHost { get; set; }

        public int ServerPort { get; set; }


        public event OnServerStarted OnServerStarted;
        public event OnServerClosed OnServerClosed;
        public event OnClientConnected OnClientConnected;
        public event OnClientLostConnection OnClientLostConnection;
        public event OnClientUpdateUI OnClientUpdateUI;
        public event OnSendingStatusChanged OnSendingStatusChanged;



        /// <summary>
        /// 启动服务
        /// </summary>
        public void start(string host,int port)
        {
            if(host == "")
            {
                host = "127.0.0.1";
            }

            if(port == 0)
            {
                port = 9510;
            }

            this.ServerHost = host;
            this.ServerPort = port;

            StartServer();
        }

        /// <summary>
        /// 关闭服务
        /// </summary>
        public void stop()
        {
            
        }


        public void sendCmd(CmdData cmdData)
        {
            Thread sendThread = new Thread(SendThreadProc);
            sendThread.IsBackground = true;
            sendThread.Start(cmdData);
        }

        private void SendThreadProc(object o)
        {
            CmdData cmdData = (CmdData)o;
            for (int i = 0; i < clients.Count; i++)
            {
                ClientHolder clientHolder = clients[i];
                if (clientHolder.ConnectionStatus == 1)
                {
                    try
                    {
                        string jsonCmd = JsonConvert.SerializeObject(cmdData, Formatting.Indented);
                        jsonCmd = jsonCmd + "$";
                        clientHolder.Socket.Send(Encoding.Default.GetBytes(jsonCmd));
                        OnSendingStatusChanged?.Invoke(i+1, clients.Count);
                        Thread.Sleep(100);
                    }
                    catch (Exception ex)
                    {
                        //TODO LOG
                        //如果是发送失败，丢失连接，标记为连接丢失
                        OnSendingStatusChanged?.Invoke(i+1, clients.Count);
                    }
                }
            }
        }




        /// <summary>
        /// 服务器启动
        /// </summary>
        protected void StartServer()
        {

            //设置线程池
            ThreadPool.SetMinThreads(4, 4);
            ThreadPool.SetMaxThreads(16, 16);

            //点击开始监听时 在服务端创建一个负责监听IP和端口号的Socket
            ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            //IPAddress ip = IPAddress.Any;
            //创建对象端口
            IPEndPoint point = new IPEndPoint(IPAddress.Parse(this.ServerHost), this.ServerPort);
            ServerSocket.Bind(point);//绑定端口号

            ServerSocket.Listen(10);//设置监听

            //调用委托
            OnServerStarted?.Invoke();

            //创建监听线程
            ListenThread = new Thread(ListenThreadProc);
            ListenThread.IsBackground = true;
            ListenThread.Start();

            //创建接收数据线程
            ReceiveDataThread = new Thread(ReceiveThreadProc);
            ReceiveDataThread.IsBackground = true;
            ReceiveDataThread.Start();
        }
        

        /// <summary>
        /// 监听连接线程
        /// </summary>
        protected void ListenThreadProc()
        {
            Console.WriteLine("ListenThreadProc running");
            while (true)
            {
                Socket clientSocket = this.ServerSocket.Accept();
                //OnClientConnected?.Invoke(this,clientSocket);


                //收到一个新的客户端连接
                ClientHolder clientHolder = new ClientHolder(clientSocket);
                clientHolder.ConnectionStatus = 1;
                this.clients.Add(clientHolder);


                //TODO LOG
            }
        }

        /// <summary>
        /// 检测收到客户端上传的数据
        /// </summary>
        protected void ReceiveThreadProc()
        {
            Console.WriteLine("ReceiveThreadProc running");
            while(true)
            {
                Thread.Sleep(500);
                for (int i = 0; i < clients.Count; i++)
                {
                    ClientHolder holder = clients[i];
                    Socket socket = holder.Socket;

                    //连接已经断开
                    if(!socket.Connected)
                    {
                        OnClientLostConnection?.Invoke(this,holder);
                        continue;
                    }
                    if (socket.Available > 0)
                    {
                        byte[] data = new byte[RECEIVE_BUFFER_SIZE];

                        try
                        {
                            socket.Receive(data);
                        }
                        catch (Exception)
                        {
                            //读取数据出现异常
                            OnClientLostConnection?.Invoke(this, holder);
                            clients.Remove(holder);
                            i = i - 1;
                            continue;
                        }
                        

                        string receiveData = Encoding.Default.GetString(data);

                        //恢复上一次读取一半的内容
                        if(tempLocalCmdString != string.Empty)
                        {
                            receiveData = tempLocalCmdString + "$" +receiveData;
                            tempLocalCmdString = string.Empty;
                        }


                        string[] cmdList = receiveData.Split('$');
                        for (int j = 0; j < cmdList.Length; j++)
                        {
                            string cmd = cmdList[j].TrimEnd('\0');
                            if(cmd == null || cmd == string.Empty)
                            {
                                continue;
                            }

                            try
                            {
                                holder.CmdData = JsonConvert.DeserializeObject<CmdData>(cmd);
                                if (holder.CmdData == null && cmd != string.Empty)
                                {
                                    tempLocalCmdString = cmd;
                                    continue;
                                }
                                if (holder.CmdData != null)
                                {
                                    ThreadPool.QueueUserWorkItem(new WaitCallback(ProcessClientCmd), holder);
                                }
                            }
                            catch (Exception)
                            {

                                
                            }
                            
                            
                        }
                        
                    }
                }
            }
        }

        protected void ProcessClientCmd(object o)
        {
            
            ClientHolder holder = (ClientHolder)o;
            if (holder.CmdData == null)
            {
                return;
            }
            CmdData cmdData = holder.CmdData;
            if (cmdData.Cmd == "UPDATE_CLIENT")
            {
                holder.ID = cmdData.ID;
                holder.CodeFrom = cmdData.CodeFrom;
                holder.CodeTo = cmdData.CodeTo;
                holder.ThreadNum = cmdData.ThreadNum;
                holder.ThreadStatus = cmdData.ThreadStatus;
                holder.CurrentCode = cmdData.CurrentCode;
                holder.Result = cmdData.Result;
                //holder.UpdateUI();
                OnClientUpdateUI?.Invoke(this, holder);
            }
            else if (cmdData.Cmd == "CLIENT_EXIT")
            {
                OnClientLostConnection?.Invoke(this, holder);
            }
            
           
        }

    }
}
